/*
 * SpriteRenderer.cpp
 *
 *  Created on: 2011/08/07
 *      Author: naoki
 */

/*
#include "StdAfx.h"
#include "NTEngine/Graphics/Texture.h"
#include "NTEngine/Graphics/Vertex.h"
#include "NTEngine/Graphics/VertexBuffer.h"
#include "NTEngine/Graphics/IndexBuffer.h"
#include "NTEngine/Math/Vector2.h"
#include "NTEngine/Math/Vector3.h"
#include "NTEngine/Math/Rectangle.h"
#include "NTEngine/Math/Matrix2x3.h"
#include "NTEngine/Math/Matrix4x4.h"
#include "NTEngine/Math/Color.h"
#include "NTEngine/Scene/SpriteRenderer.h"
#include "NTEngine/Graphics/Manager.h"
#include "NTEngine/Base/ReferenceCounter.h"
#include <algorithm>
using namespace std;

namespace NTEngine {
using namespace Graphics;
using namespace Math;
namespace Scene {

//ソート用比較関数
template< class T >
bool pointerLess( const T* a, const T* b ) {
	return ( *a < *b );
}

class SpriteRenderer::Impl : public ReferenceCounter {
public:
	Impl( int bufferSize )
	: mVertexBuffer()
	, mIndexBuffer()
	, mSpriteObjectNumber( 0 )
	, mMaxBufferSize( bufferSize )
	, mBatch( 0x0 )
	, mDrawList( 0x0 ) {
		// インデックスバッファの関係で
		ASSERT( ( bufferSize <= 0xFFFF / 6 ) && "index buffer over size." );
		// スクリーン→射影に戻す変換行列 3_3だけ0にしとく
		mScreenToProjection.identity();
		mScreenToProjection.m33 = 0.F;
		// グラフィックスバッファ
		mVertexBuffer = VertexBuffer::create( bufferSize * 4, VertexBuffer::FORMAT_SPRITE );
		mIndexBuffer = IndexBuffer::create( bufferSize * 6 );
		unsigned short* buf = NEW unsigned short[ mIndexBuffer.indexNumber() ];
		for ( int i = 0, j = 0; i < bufferSize; ++i, j += 6 ) {
			buf[j + 0] = (i * 4);
			buf[j + 1] =
			buf[j + 4] = (i * 4) + 1;
			buf[j + 2] =
			buf[j + 3] = (i * 4) + 2;
			buf[j + 5] = (i * 4) + 3;
		}
		mIndexBuffer.store( buf );
		SAFE_DELETE_ARRAY( buf );

		// アプリ側バッファ
		mBatch = NEW Batch[ bufferSize ];
		mDrawList = NEW Batch*[ bufferSize ];
		for ( int i = 0; i < bufferSize; ++i ) {
			mDrawList[ i ] = 0x0;
		}
	}

	~Impl() {
		SAFE_DELETE_ARRAY( mDrawList );
		SAFE_DELETE_ARRAY( mBatch );
	}

	void add(
		Texture texture,
		const Vector2& position,
		const Rectangle& source,
		const Vector2& scale,
		float depth,
		Color32 color )
	{
		if ( mSpriteObjectNumber < mMaxBufferSize ) {
			float texcoord[ 4 ] = {
				static_cast<float>( source.x ),
				static_cast<float>( source.y ),
				static_cast<float>( source.x + source.w ),
				static_cast<float>( source.y + source.h )
			};
			float size[ 4 ] = { 0, 0,
				static_cast<float>( source.w ),
				static_cast<float>( source.h )
			};
			float texWidthRate = 1.F / texture.width();
			float texHeightRate = 1.F / texture.height();
			mBatch[ mSpriteObjectNumber ].t = texture;
			BaseStreamSource* v = mBatch[ mSpriteObjectNumber ].v;
			int id0, id1;
			for ( int i = 0; i < 4; ++i ) {
				// テクスチャコード計算
				id0 = ( i & 0x1 ) << 1;
				id1 = ( i & 0x2 ) + 1;
				// 仮バッファに保存
				v[ i ].uv.x = texcoord[ id0 ] * texWidthRate;
				v[ i ].uv.y = texcoord[ id1 ] * texHeightRate;
				v[ i ].position.x = position.x + size[ id0 ] * scale.x;
				v[ i ].position.y = position.y + size[ id1 ] * scale.y;
				v[ i ].position.z = depth;
				v[ i ].position.w = 1.F;
				v[ i ].color = color;
			}
			v = 0;
			// 描画リストに登録
			mDrawList[ mSpriteObjectNumber ] = &mBatch[ mSpriteObjectNumber ];
			++mSpriteObjectNumber;
		}
	}

	void add(
		Texture texture,
		const Matrix2x3& transform,
		const Rectangle& source,
		float depth,
		Color32 color )
	{
		if ( mSpriteObjectNumber < mMaxBufferSize ) {
			// ループしやすいよう変換
			float texcoord[ 4 ] = {
				static_cast<float>( source.x ),
				static_cast<float>( source.y ),
				static_cast<float>( source.x + source.w ),
				static_cast<float>( source.y + source.h )
			};
			float size[ 4 ] = { 0, 0,
				static_cast<float>( source.w ),
				static_cast<float>( source.h )
			};
			float texWidthRate = 1.F / texture.width();
			float texHeightRate = 1.F / texture.height();
			Vector2 tmp;
			mBatch[ mSpriteObjectNumber ].t = texture;
			BaseStreamSource* v = mBatch[ mSpriteObjectNumber ].v;
			int id0, id1;
			for ( int i = 0; i < 4; ++i ) {
				id0 = ( i & 0x1 ) << 1;
				id1 = ( i & 0x2 ) + 1;
				// テクスチャコード計算
				tmp.x = size[ id0 ];
				tmp.y = size[ id1 ];
				// 仮バッファに保存
				v[ i ].uv.x = texcoord[ id0 ] * texWidthRate;
				v[ i ].uv.y = texcoord[ id1 ] * texHeightRate;
				transform.multiply( tmp, &tmp );
				v[ i ].position.x = tmp.x;
				v[ i ].position.y = tmp.y;
				v[ i ].position.z = depth;
				v[ i ].position.w = 1.F;
				v[ i ].color = color;
			}
			v = 0x0;
			// 描画リストに登録
			mDrawList[ mSpriteObjectNumber ] = &mBatch[ mSpriteObjectNumber ];
			++mSpriteObjectNumber;
		}
	}

	void draw()
	{
		if ( mSpriteObjectNumber > 0 ) {
			// クイックソートしていただく
			sort( mDrawList, mDrawList + mSpriteObjectNumber, pointerLess<Batch> );
			// バッファロックして書き込みます
			{
				BaseStreamSource* buf;
				mVertexBuffer.lock( &buf );
				const BaseStreamSource* src;
				BaseStreamSource* dst;
				for ( int i = 0; i < mSpriteObjectNumber; ++i ) {
					for ( int j = 0; j < 4; ++j ) {
						src = ( mDrawList[ i ]->v + j );
						dst = ( buf + j );
						dst->uv.x = src->uv.x;
						dst->uv.y = src->uv.y;
						dst->position.x = src->position.x;
						dst->position.y = src->position.y;
						dst->position.z = src->position.z;
						dst->position.w = src->position.w;
						dst->color = src->color;
					}
					buf += 4;
				}
				mVertexBuffer.unlock( &buf );
			}
			// 描画
			Graphics::Manager gm;
			gm.setLightingMode( Graphics::LIGHTING_NONE );
			Matrix3x4 w;
			w.identity();
			gm.setWorldMatrix( w );
			float w = static_cast<float>( gm.width() );
			float h = static_cast<float>( gm.height() );
			mScreenToProjection.m11 = +2.F / w;
			mScreenToProjection.m22 = -2.F / h;
			mScreenToProjection.m14 = -1.F - (0.5F / w); // 0.5ピクセルずらし
			mScreenToProjection.m24 = +1.F + (0.5F / h); // 0.5ピクセルずらし
			gm.setProjectionViewMatrix( &mScreenToProjection );
			gm.setVertexBuffer( mVertexBuffer );
			gm.setIndexBuffer( mIndexBuffer );
			gm.setShadeMode( SHADE_FLAT );
			gm.setTextureFilter( TEXF_POINT );
			gm.setBlendMode( BLEND_LINEAR );
			gm.setFillMode( FILL_SOLID );
			gm.setCullMode( CULL_BACK );
			Texture lastTexture = mDrawList[ 0 ]->t;
			int offset = 0, spriteNumber = 2;
			for ( int i = 1; i < mSpriteObjectNumber; ++i ) {
				Batch* b = mDrawList[ i ];
				if ( lastTexture != b->t ) {
					gm.setTexture( lastTexture );
					gm.drawIndexed( offset, spriteNumber, PT_LIST );
					lastTexture = b->t;
					offset += spriteNumber * 3;
					spriteNumber = 2;
				}
				spriteNumber += 2;
			}
			gm.setTexture( lastTexture );
			gm.drawIndexed( offset, spriteNumber, PT_LIST );
			mSpriteObjectNumber = 0;
		}
	}

	int size() const {
		return mSpriteObjectNumber;
	}

	int capacity() const {
		return mMaxBufferSize;
	}

private:
	// スプライト情報をまとめたもの
	class Batch {
	public:
		Matrix2x3 transform;
		float depth;
		Rectangle rect;
		unsigned long color;

		Texture t;

		bool operator<( const Batch& a ) const {
			// 4頂点の奥行きは同じなためとりあえず先頭を使います。
			float d1 = v->position.z;
			float d2 = a.v->position.z;
			if ( depth > a.depth ) {
				return true;
			} else if ( depth < a.depth ){
				return false;
			} else if ( t < a.t ) {
				return true;
			}
			return false;
		}
	};
	VertexBuffer mVertexBuffer; //!< 頂点バッファ
	IndexBuffer mIndexBuffer; //!< インデックスバッファ
	Matrix4x4 mScreenToProjection; //!< スクリーン変換用行列
	Batch* mBatch; //!< スプライト情報のバッファ
	Batch** mDrawList; //!< 描画リスト
	int mSpriteObjectNumber; //!< スプライトの数
	const int mMaxBufferSize; //!< 最大スプライト数
};

SpriteRenderer SpriteRenderer::create( int bufferSize ) {
	SpriteRenderer ret;
	ret.mImpl = NEW Impl( bufferSize );
	return ret;
}

void SpriteRenderer::add( Texture texture, int x, int y, float depth ) {
	ASSERT( mImpl && "Empth Object" );
	Math::Vector2 position( x, y );
	Math::Rectangle source( 0, 0, texture.width(), texture.height() );
	Math::Color color( 1.F, 1.F, 1.F, 1.F );
	Math::Vector2 scale( 1.F, 1.F );
	m_Impl->add( texture, position, source, scale, depth, color );
}

void SpriteRenderer::add(
	Texture texture,
	const Math::Vector2& position,
	const Math::Rectangle& source,
	const Math::Color& color,
	float depth )
{
	ASSERT( m_Impl && "Empth Object" );
	Vector2 scale( 1.F, 1.F );
	m_Impl->add( texture, position, source, scale, depth, color );
}

void SpriteRenderer::add(
	Texture texture,
	const Math::Vector2& position,
	const Math::Rectangle& source,
	const Math::Color& color,
	const Math::Vector2& scale,
	float depth )
{
	ASSERT( mImpl && "Empth Object" );
	mImpl->add( texture, position, source, scale, depth, color );
}

void SpriteRenderer::add(
	Graphics::Texture texture,
	const Math::Vector2& position,
	const Math::Rectangle& source,
	const Math::Color& color,
	const Math::Vector2& origin,
	float angle,
	float depth,
	const Math::Vector2& scale )
{
	ASSERT( mImpl && "Empth Object" );
	Matrix2x3 transform;
	transform.setTranslation( position );
	transform.rotate( angle );
	transform.scale( scale );
	transform.translate( origin );
	mImpl->add( texture, transform, source, depth, color );
}

void SpriteRenderer::draw() {
	ASSERT( mImpl && "Empth Object" );
	mImpl->draw();
}

int SpriteRenderer::size() const {
	ASSERT( mImpl && "Empth Object" );
	return mImpl->size();
}

int SpriteRenderer::capacity() const {
	ASSERT( mImpl && "Empth Object" );
	return mImpl->capacity();
}

#define TYPE SpriteRenderer
#include "NTEngine/Base/Impl/ReferenceCounterTemplate.h"

} // namespace Scene
} // namespace NTEngine
// EOF
*/
